//***************************************************************************
//   Copyright(c)2020, Xidian University D405.
//   All rights reserved
//
//   IP Name         :   Null
//   File name       :   np_dma_rx_desc.v
//   Module name     :   np_dma_rx_desc
//   Author          :   Wang Zekun
//   Date            :   2022/04/27
//   Version         :   v1.2 
//   Verison History :   v1.0/v1.1/v1.2
//   Edited by       :   Wang Zekun
//   Modification history : v1.0 Initial revision
//                          v1.1 desc address 40bit,data address 40bit, data address align 64B--0x40--0b0100_0000
//                               desc new function IOC
//                          v1.2 data address in descriptor is 40bit
// ----------------------------------------------------------------------------
// Version 1.2       Date(2022/04/27)
// Abstract : rx descriptor parse and construct
//-----------------------------------------------------------------------------
// Programmer's model
//                    rdesc_i_0--------packet address[31:0]
//                    rdesc_i_1--------{reserved[31:8],packet address[7:0]}
//                    rdesc_i_2--------reserved[31:0]
//                    rdesc_i_3--------{own,ioc,port,reserved[28:20],length[19:0]}
//-----------------------------------------------------------------------------
//interface list :
//
module np_dma_rx_desc #(
    parameter AXI_ADDR_WIDTH = 32,
    parameter AXI_LIB_WIDTH = 20
  )(
  input  wire                               clk_i,
  input  wire                               resetn_i,

  input  wire                               rx_start_en_i,    // from ahb decode,only once
  input  wire                               rx_start_circle_i,

  output wire                               rx_descfifo_read_en_o,
  input  wire                               rx_desc_fifo_rd_empty_i,
  input  wire                               rx_desc_read_valid_i,
  input  wire [127:0]                       rx_desc_i,

  input  wire                               rx_fifo_empty_i,
  input  wire                               rx_patcket_length_valid_i,
  input  wire [AXI_LIB_WIDTH-1 :0]          rx_patcket_length_i,
  input  wire                               rx_port_status_i,
  
  output wire                               rx_desc_constr_valid_o,
  output wire [127:0]                       rx_desc_o,

  output wire                               rx_desc_parse_valid_o,
  output wire [AXI_ADDR_WIDTH-1:0]          rx_packet_address_o,
  output wire [AXI_LIB_WIDTH-1 :0]          rx_patcket_length_o,
  output wire                               rx_desc_ioc_o,
  output wire                               rx_desc_own_o

);

  reg  [127:0]                        rx_desc_i_r;
  wire [31:0]                         rdesc_i_0;
  wire [31:0]                         rdesc_i_1;
  wire [31:0]                         rdesc_i_2;
  wire [31:0]                         rdesc_i_3;

  reg  [31:0]                         rdesc_o_0;
  reg  [31:0]                         rdesc_o_1;
  reg  [31:0]                         rdesc_o_2;
  reg  [31:0]                         rdesc_o_3;
  
  reg                                 rx_desc_parse_valid_r;
  reg                                 rx_desc_parse_valid_r1;
  reg                                 rx_desc_parse_unvalid_r1;
  reg                                 rx_start_circle_r;
  reg                                 rx_desc_construct;
  reg                                 rx_desc_construct_r;
  reg                                 rx_fifo_empty_r;

  reg  [AXI_ADDR_WIDTH-1:0]           packet_init_address_record;
  wire [6:0]                          packet_low_address;
  reg  [AXI_LIB_WIDTH-1 :0]           rx_patcket_length_r;
  reg                                 first_read_desc;
  
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      first_read_desc <= 1'b0;
    end
    else if(rx_desc_construct) begin
      first_read_desc <= 1'b0;
    end
    else if(rx_start_en_i) begin
      first_read_desc <= 1'b1;
    end
    else begin
      first_read_desc <= first_read_desc;
    end
  end

  always @(*) begin
      rx_patcket_length_r = rx_patcket_length_i;
  end

  // packet address caculate
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      packet_init_address_record <= {AXI_ADDR_WIDTH{1'b0}};
    end
    else if(rx_desc_construct) begin
      packet_init_address_record <= rx_packet_address_o + {{AXI_ADDR_WIDTH-AXI_LIB_WIDTH{1'b0}},rx_patcket_length_r[AXI_LIB_WIDTH-1:6],6'b0} + packet_low_address;
    end
    else begin
      packet_init_address_record <= packet_init_address_record;
    end
  end
  assign packet_low_address = |rx_patcket_length_r[5:0] ? 7'b100_0000 : 7'b000_0000;

  // read desc enable lock
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      rx_start_circle_r <= 1'b0;
    end
    else if(rx_descfifo_read_en_o) begin
      rx_start_circle_r <= 1'b0;
    end
    else if(rx_start_circle_i) begin // rx_desc_construct_r | rx_start_en_i
      rx_start_circle_r <= 1'b1;
    end
    else begin
      rx_start_circle_r <= rx_start_circle_r;
    end
  end
  
  assign rx_descfifo_read_en_o = rx_fifo_empty_r | rx_desc_fifo_rd_empty_i ? 1'b0 : rx_start_circle_r;

  //***********************
  //********parse procedure
  //***********************
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      rx_desc_i_r <= {128{1'b0}};
    end
    else if(rx_desc_read_valid_i) begin
      rx_desc_i_r <= rx_desc_i;
    end
    else begin
      rx_desc_i_r <= rx_desc_i_r;
    end
  end

  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      rx_desc_parse_valid_r <= 1'b0;
      rx_desc_parse_valid_r1 <= 1'b0;
      rx_desc_parse_unvalid_r1 <= 1'b0;
    end
    else begin
      rx_desc_parse_valid_r <= rx_desc_read_valid_i;
      rx_desc_parse_valid_r1 <= rx_desc_parse_valid_r;   //own = 1'b1 mean owned by CPU,parse is valid,transmit continue
      rx_desc_parse_unvalid_r1 <= rx_desc_parse_valid_r & ~rx_desc_own_o;//own = 1'b0 mean owned by DMA,parse is unvalid,transmit suspend
    end
  end

  assign rdesc_i_0 = rx_desc_i_r[31:0];
  assign rdesc_i_1 = rx_desc_i_r[63:32];
  assign rdesc_i_2 = rx_desc_i_r[95:64];
  assign rdesc_i_3 = rx_desc_i_r[127:96];

  assign rx_packet_address_o   = first_read_desc ? {rdesc_i_1[7:0],rdesc_i_0} : packet_init_address_record;
  assign rx_desc_ioc_o         = rdesc_i_3[30];
  assign rx_desc_own_o         = rdesc_i_3[31];//1'b1;//
  assign rx_desc_parse_valid_o = rx_desc_parse_valid_r1;

  //***********************
  //********construct procedure
  //***********************
    // rdesc0 construct
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      rdesc_o_0 <= {32{1'b0}};
      rdesc_o_1[7:0] <= {8{1'b0}};
    end
    else if(rx_desc_construct) begin
      rdesc_o_0 <= rx_packet_address_o[31:0];
      rdesc_o_1[7:0] <= rx_packet_address_o[AXI_ADDR_WIDTH-1:32];
    end
    else begin
      rdesc_o_0 <= rdesc_o_0;
      rdesc_o_1[7:0] <= rdesc_o_1[7:0];
    end
  end

    // rdesc1/2/3 reserved
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      rdesc_o_1[31:8]  <= {24{1'b0}};
      rdesc_o_2        <= {32{1'b0}};
      rdesc_o_3[28:20] <= {10{1'b0}};
    end
    else if(rx_desc_construct) begin
      rdesc_o_1[31:8]  <= rdesc_i_1[31:8];
      rdesc_o_2        <= rdesc_i_2;
      rdesc_o_3[28:20] <= rdesc_i_3[28:20];
    end
    else begin
      rdesc_o_1[31:8]  <= rdesc_o_1[31:8] ;
      rdesc_o_2        <= rdesc_o_2;
      rdesc_o_3[28:20] <= rdesc_o_3[28:20];
    end
  end

    // rdesc3 construct
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      rdesc_o_3[31:29] <= 3'b000;
      rdesc_o_3[19:0] <= {20{1'b0}};
    end
    else if(rx_desc_construct) begin
      rdesc_o_3[31:29] <= {1'b0,rx_desc_ioc_o,rx_port_status_i};
      rdesc_o_3[19:0] <= {{20-AXI_LIB_WIDTH{1'b0}},rx_patcket_length_i};
    end
    else begin
      rdesc_o_3[31:29] <= rdesc_o_3[31:29];
      rdesc_o_3[19:0] <= rdesc_o_3[19:0];
    end
  end

  assign rx_desc_o = rx_desc_constr_valid_o ? {rdesc_o_3,rdesc_o_2,rdesc_o_1,rdesc_o_0} : {128{1'b0}};
  reg rx_fifo_empty_r0;
  always @(posedge clk_i or negedge resetn_i) begin
    if(~resetn_i) begin
      rx_desc_construct   <= 1'b0;
      rx_desc_construct_r <= 1'b0;
      rx_fifo_empty_r0    <= 1'b1;
      rx_fifo_empty_r     <= 1'b1;
    end
    else begin
      rx_desc_construct   <= rx_desc_parse_valid_r1;
      rx_desc_construct_r <= rx_desc_construct;
      rx_fifo_empty_r0    <= rx_fifo_empty_i;
      rx_fifo_empty_r     <= rx_fifo_empty_r0;
    end
  end
  
  assign rx_desc_constr_valid_o = rx_desc_construct_r;
  assign rx_patcket_length_o    = rx_patcket_length_r;

endmodule
